///	<summary>
///	  Unit testing for libraries in DAttributes.Ini unit
///	</summary>
///	<remarks>
///	  Uses DUnit testing framework
///	</remarks>
unit DAttributes.Ini.Test;

(*******************************************************************************
  MIT License

  Copyright (c) 2013 - Luthfi Harahap

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE
*******************************************************************************)

interface

uses
  SysUtils
  , Classes
  , TestFramework
  , DAttributes.Ini
  ;

const
  SECTION01_NAME       = 'SECTION001';
  SECTION02_NAME       = 'NUM2SECTION02';
  SECTION03_NAME       = 'SECT_NUMBER3';

  PROTECTED_INT_PROPERTY_NAME = 'ProtectedIntProperty';
  PROTECTED_INT_PROPERTY_DEFAULTVALUE = '123';
  PROTECTED_INT_PROPERTY_TESTVALUE = '7890';

  PROTECTED_STRING_PROPERTY_NAME = 'ProtectedStringProperty';
  PROTECTED_STRING_PROPERTY_DEFAULTVALUE = 'STRING';
  PROTECTED_STRING_PROPERTY_TESTVALUE = 'TESTTESTTEST';

  PUBLIC_INT_PROPERTY_NAME = 'PublicIntProperty';
  PUBLIC_INT_PROPERTY_DEFAULTVALUE = '456';
  PUBLIC_INT_PROPERTY_TESTVALUE = '88354';

  PUBLIC_STRING_PROPERTY_NAME = 'PublicStringProperty';
  PUBLIC_STRING_PROPERTY_DEFAULTVALUE = 'WSTRING';
  PUBLIC_STRING_PROPERTY_TESTVALUE = 'QWERTY';

  PUBLIC_STRING_PROPWITHSECTION_NAME = 'PublicStringPropWithSection';
  PUBLIC_STRING_PROPWITHSECTION_DEFAULTVALUE = 'DEFAULTDEFAULTDEFAULT';
  PUBLIC_STRING_PROPWITHSECTION_TESTVALUE = 'TESTPublicStringPropWithSectionTEST';

  PUBLIC_STRING_PROPWITHSECTION_2_NAME = 'PublicStringPropWithSection2';
  PUBLIC_STRING_PROPWITHSECTION_2_DEFAULTVALUE = '22DEF22AULT22DEF22AULT222DEF22AUL22T';
  PUBLIC_STRING_PROPWITHSECTION_2_TESTVALUE = 'TESTPublicTESTStringTESTPropTESTWithTESTSection2TEST';

type
  ///	<summary>
  ///	  Dummy class for testing custom attributes
  ///	</summary>
  [IniSection(SECTION01_NAME)]
  TIniStoreTestClass=class
  private
    FProtectedIntProperty: Integer;
    FProtectedStringProperty: string;
    FPublicStringProperty: string;
    FPublicIntProperty: Integer;
    FPublicStringPropWithSection: string;
    FPublicStringPropWithSection2: string;
  protected
    [IniValue(PROTECTED_INT_PROPERTY_NAME, PROTECTED_INT_PROPERTY_DEFAULTVALUE)]
    property ProtectedIntProperty: Integer read FProtectedIntProperty write FProtectedIntProperty;
    [IniValue(PROTECTED_STRING_PROPERTY_NAME, PROTECTED_STRING_PROPERTY_DEFAULTVALUE)]
    property ProtectedStringProperty: string read FProtectedStringProperty write FProtectedStringProperty;
  public
    [IniValue(PUBLIC_INT_PROPERTY_NAME, PUBLIC_INT_PROPERTY_DEFAULTVALUE)]
    property PublicIntProperty: Integer read FPublicIntProperty write FPublicIntProperty;
    [IniValue(PUBLIC_STRING_PROPERTY_NAME, PUBLIC_STRING_PROPERTY_DEFAULTVALUE)]
    property PublicStringProperty: string read FPublicStringProperty write FPublicStringProperty;
    [IniValue(SECTION02_NAME, PUBLIC_STRING_PROPWITHSECTION_NAME, PUBLIC_STRING_PROPWITHSECTION_DEFAULTVALUE)]
    property PublicStringPropWithSection: string read FPublicStringPropWithSection write FPublicStringPropWithSection;
    [IniValue(SECTION03_NAME, PUBLIC_STRING_PROPWITHSECTION_2_NAME, PUBLIC_STRING_PROPWITHSECTION_2_DEFAULTVALUE)]
    property PublicStringPropWithSection2: string read FPublicStringPropWithSection2 write FPublicStringPropWithSection2;
  end;

  ///	<summary>
  ///	  Unit tests for IniSection attribute class
  ///	</summary>
  TIniSectionTest=class(TTestCase)
  protected
    procedure SetUp; override;
    procedure TearDown; override;
  published
    procedure Test_Create;
    procedure Test_Storing;
    procedure Test_Loading;
  end;

  ///	<summary>
  ///	  Unit tests for IniValue attribute class
  ///	</summary>
  TIniValueTest=class(TTestCase)
  published
    procedure Test_DefaultValues;
    procedure Test_ValuesWithSectionName;
    procedure Test_Storing;
  end;

implementation

uses
  IniFiles
  ;

function GetIniFilename: string;
begin
  Result := ExtractFilePath(ParamStr(0)) + 'test.ini';
end;

{ TIniSectionTest }

procedure TIniSectionTest.SetUp;
begin
  inherited;
end;

procedure TIniSectionTest.TearDown;
begin
  inherited;
end;

procedure TIniSectionTest.Test_Create;
const
  cSectionName = 'SECTION_TEST';
var
  vIniSection: IniSection;
begin
  vIniSection := IniSection.Create(cSectionName);
  try
    Check(vIniSection.Section = cSectionName);
  finally
    vIniSection.Free;
  end;
end;

procedure TIniSectionTest.Test_Loading;
var
  vIni: TIniFile;
  vIniFilename: string;
  vTestObj: TIniStoreTestClass;
begin
  vIniFilename := GetIniFilename;
  if FileExists(vIniFilename) then
    DeleteFile(vIniFilename);

  vIni := TIniFile.Create(vIniFilename);
  try
    vIni.WriteInteger(SECTION01_NAME, PUBLIC_INT_PROPERTY_NAME, StrToInt(PUBLIC_INT_PROPERTY_TESTVALUE));
    vIni.WriteString(SECTION01_NAME, PUBLIC_STRING_PROPERTY_NAME, PUBLIC_STRING_PROPERTY_TESTVALUE);

    vIni.WriteInteger(SECTION01_NAME, PROTECTED_INT_PROPERTY_NAME, StrToInt(PROTECTED_INT_PROPERTY_TESTVALUE));
    vIni.WriteString(SECTION01_NAME, PROTECTED_STRING_PROPERTY_NAME, PROTECTED_STRING_PROPERTY_TESTVALUE);
  finally
    vIni.Free;
  end;

  vTestObj := TIniStoreTestClass.Create;
  try
    DAttributes.Ini.LoadFromIniFile(vIniFilename, vTestObj);
    Check(vTestObj.PublicIntProperty = StrToInt(PUBLIC_INT_PROPERTY_TESTVALUE));
    Check(vTestObj.PublicStringProperty = PUBLIC_STRING_PROPERTY_TESTVALUE);
  finally
    vTestObj.Free;
  end;
end;

procedure TIniSectionTest.Test_Storing;
var
  vIni: TIniFile;
  vTestObj: TIniStoreTestClass;
  vSections: TStrings;
  vIniFilename: string;
begin
  vIniFilename := GetIniFilename;
  if FileExists(vIniFilename) then
    DeleteFile(vIniFilename);

  vTestObj := TIniStoreTestClass.Create;
  try
    vTestObj.ProtectedIntProperty := StrToInt(PROTECTED_INT_PROPERTY_DEFAULTVALUE);
    vTestObj.ProtectedStringProperty := PROTECTED_STRING_PROPERTY_DEFAULTVALUE;
    vTestObj.PublicIntProperty := StrToInt(PUBLIC_INT_PROPERTY_DEFAULTVALUE);
    vTestObj.PublicStringProperty := PUBLIC_STRING_PROPERTY_DEFAULTVALUE;

    DAttributes.Ini.SaveToIniFile(vIniFilename, vTestObj);
  finally
    vTestObj.Free;
  end;

  vIni := TIniFile.Create(vIniFilename);
  try
    vSections := TStringList.Create;
    try
      vIni.ReadSections(vSections);
      Check(vSections.IndexOf(SECTION01_NAME) > -1);
    finally
      vSections.Free;
    end;
  finally
    vIni.Free;
  end;
end;

{ TIniValueTest }

procedure TIniValueTest.Test_DefaultValues;
var
  vTestObj: TIniStoreTestClass;
begin
  Randomize;
  vTestObj := TIniStoreTestClass.Create;
  try
    DAttributes.Ini.LoadDefValues(vTestObj);
    Check(vTestObj.PublicIntProperty=StrToInt(PUBLIC_INT_PROPERTY_DEFAULTVALUE));
    Check(vTestObj.PublicStringProperty=PUBLIC_STRING_PROPERTY_DEFAULTVALUE);
    Check(vTestObj.PublicStringPropWithSection=PUBLIC_STRING_PROPWITHSECTION_DEFAULTVALUE);
    Check(vTestObj.PublicStringPropWithSection2=PUBLIC_STRING_PROPWITHSECTION_2_DEFAULTVALUE);

    repeat
      vTestObj.PublicIntProperty := Random(MaxInt);
      // The following duplication is intentional to make sure the property
      // was changed at least twice
      vTestObj.PublicIntProperty := Random(MaxInt);
    until vTestObj.PublicIntProperty <> StrToInt(PUBLIC_INT_PROPERTY_DEFAULTVALUE);

    repeat
      vTestObj.PublicStringProperty := IntToStr(Random(MaxInt));
      // The following duplication is intentional to make sure the property
      // was changed at least twice
      vTestObj.PublicStringProperty := IntToStr(Random(MaxInt));
    until vTestObj.PublicStringProperty <> PUBLIC_STRING_PROPERTY_DEFAULTVALUE;

    repeat
      vTestObj.PublicStringPropWithSection := IntToStr(Random(MaxInt));
      // The following duplication is intentional to make sure the property
      // was changed at least twice
      vTestObj.PublicStringPropWithSection := IntToStr(Random(MaxInt));
    until vTestObj.PublicStringPropWithSection <> PUBLIC_STRING_PROPWITHSECTION_DEFAULTVALUE;

    repeat
      vTestObj.PublicStringPropWithSection2 := IntToStr(Random(MaxInt));
      // The following duplication is intentional to make sure the property
      // was changed at least twice
      vTestObj.PublicStringPropWithSection2 := IntToStr(Random(MaxInt));
    until vTestObj.PublicStringPropWithSection2 <> PUBLIC_STRING_PROPWITHSECTION_2_DEFAULTVALUE;

    DAttributes.Ini.LoadDefValues(vTestObj);
    Check(vTestObj.PublicIntProperty=StrToInt(PUBLIC_INT_PROPERTY_DEFAULTVALUE));
    Check(vTestObj.PublicStringProperty=PUBLIC_STRING_PROPERTY_DEFAULTVALUE);
    Check(vTestObj.PublicStringPropWithSection=PUBLIC_STRING_PROPWITHSECTION_DEFAULTVALUE);
    Check(vTestObj.PublicStringPropWithSection2=PUBLIC_STRING_PROPWITHSECTION_2_DEFAULTVALUE);

  finally
    vTestObj.Free;
  end;
end;

procedure TIniValueTest.Test_Storing;
var
  vIni: TIniFile;
  vTestObj: TIniStoreTestClass;
  vSections: TStrings;
  vValueNames: TStringList;
  vIniFilename: string;
begin
  vIniFilename := GetIniFilename;
  if FileExists(vIniFilename) then
    DeleteFile(vIniFilename);

  vTestObj := TIniStoreTestClass.Create;
  try
    DAttributes.Ini.LoadDefValues(vTestObj);
    DAttributes.Ini.SaveToIniFile(vIniFilename, vTestObj);
  finally
    vTestObj.Free;
  end;

  vIni := TIniFile.Create(vIniFilename);
  try
    vSections := TStringList.Create;
    try
      vIni.ReadSections(vSections);
      Check(vSections.IndexOf(SECTION01_NAME) > -1);
      Check(vSections.IndexOf(SECTION02_NAME) > -1);
      Check(vSections.IndexOf(SECTION03_NAME) > -1);

      vValueNames := TStringList.Create;
      try
        vIni.ReadSectionValues(SECTION01_NAME, vValueNames);
        Check(vValueNames.Values[PUBLIC_INT_PROPERTY_NAME] = PUBLIC_INT_PROPERTY_DEFAULTVALUE);
        Check(vValueNames.Values[PUBLIC_STRING_PROPERTY_NAME] = PUBLIC_STRING_PROPERTY_DEFAULTVALUE);
        vValueNames.Clear;
        vIni.ReadSectionValues(SECTION02_NAME, vValueNames);
        Check(vValueNames.Values[PUBLIC_STRING_PROPWITHSECTION_NAME] = PUBLIC_STRING_PROPWITHSECTION_DEFAULTVALUE);
        vValueNames.Clear;
        vIni.ReadSectionValues(SECTION03_NAME, vValueNames);
        Check(vValueNames.Values[PUBLIC_STRING_PROPWITHSECTION_2_NAME] = PUBLIC_STRING_PROPWITHSECTION_2_DEFAULTVALUE);
      finally
        vValueNames.Free;
      end;
    finally
      vSections.Free;
    end;
  finally
    vIni.Free;
  end;
end;

procedure TIniValueTest.Test_ValuesWithSectionName;
var
  vIni: TIniFile;
  vTestObj: TIniStoreTestClass;
  vSections: TStrings;
  vIniFilename: string;
begin
  vIniFilename := GetIniFilename;
  if FileExists(vIniFilename) then
    DeleteFile(vIniFilename);

  vTestObj := TIniStoreTestClass.Create;
  try
    DAttributes.Ini.LoadDefValues(vTestObj);
    DAttributes.Ini.SaveToIniFile(vIniFilename, vTestObj);
  finally
    vTestObj.Free;
  end;

  vIni := TIniFile.Create(vIniFilename);
  try
    vSections := TStringList.Create;
    try
      vIni.ReadSections(vSections);
      Check(vSections.IndexOf(SECTION01_NAME) > -1);
      Check(vSections.IndexOf(SECTION02_NAME) > -1);
      Check(vSections.IndexOf(SECTION03_NAME) > -1);
    finally
      vSections.Free;
    end;
  finally
    vIni.Free;
  end;
end;

initialization
  TestFramework.RegisterTest('', TIniSectionTest.Suite);
  TestFramework.RegisterTest('', TIniValueTest.Suite);
end.
